package ac.github.oa.internal.base.event.impl

import ac.github.oa.OriginAttribute
import ac.github.oa.api.OriginAttributeAPI
import ac.github.oa.api.event.entity.ProxyDamageEvent
import ac.github.oa.internal.base.event.EventMemory
import ac.github.oa.internal.core.attribute.Attribute
import ac.github.oa.internal.core.attribute.AttributeData
import ac.github.oa.internal.core.attribute.AttributeType
import org.bukkit.entity.LivingEntity
import taboolib.common5.Coerce

class DamageMemory(val event: ProxyDamageEvent) : EventMemory {

    var attacker
        get() = event.attacker
        set(value) {
            event.attacker = value
        }

    @Deprecated("to victim")
    var injured = event.entity

    var victim
        get() = event.entity
        set(value) {
            event.entity = value
        }

    var attackerData = if (event.isProjectile && event.damager.hasMetadata("attributeData")) {
        event.damager.getMetadata("attributeData").first().value() as AttributeData
    } else {
        OriginAttributeAPI.getAttributeData(attacker)
    }

    var cause : String
        get() = event.customCause
        set(value) {
            event.customCause = value
        }

    var victimData = OriginAttributeAPI.getAttributeData(victim)

    var pointer = AttributeType.ATTACK

    fun isPointerAttack() = pointer == AttributeType.ATTACK

    fun isPointerInjured() = !isPointerAttack()

    /**
     * 攻击力度
     */
    var vigor = when (event.damage) {
        -1.0 -> 1.0
        else -> when (val attackDamage = attacker.genericAttackDamage) {

            null -> 1.0

            0.0 -> 0.0

            else -> (event.damage / attackDamage).coerceAtLeast(0.0).coerceAtMost(1.0)

        }

    }

    companion object {

        val EMPTY_SOURCE = Source(-1, 0.0)

        val LivingEntity.genericAttackDamage: Double?
            get() = getAttribute(org.bukkit.attribute.Attribute.GENERIC_ATTACK_DAMAGE)?.value

    }

    // 如果关闭原版属性 并且攻击者是玩家 则启用
    var damage = if (OriginAttribute.original) {
        0.1
    } else event.damage


    val labels = mutableMapOf<Any, Any>()

    operator fun get(key: Any) = labels[key]

    operator fun set(key: Any,value: Any) = setLabel(key, value)

    fun setLabel(key: Any, value: Any): DamageMemory {
        labels[key] = value
        return this
    }

    fun addDamage(attribute: Attribute, value: Double): DamageMemory {
        return addDamage(attribute.toLocalName(), value)
    }

    val totalDamage: Double
        get() = (getDamageSources().sumOf { it.value } + damage)

    fun getDamage(key: Any): Double {
        val source = getDamageSources().firstOrNull { it.any == key } ?: EMPTY_SOURCE
        return Coerce.toDouble(source.value)
    }

    fun getDamageSources(): List<Source> {
        return labels.keys.filterIsInstance<Source>()
    }


    fun getDamageSource(any: Any) = getDamageSources().firstOrNull { it.any == any }

    inline fun <reified T> damageSources() = getDamageSources().filter {
        it !is FinalSource && it.any is T
    }

    fun dynamicDamageSource(callback: (Source) -> Unit) {
        getDamageSources().forEach {
            if (it !is FinalSource) {
                callback(it)
            }
        }
    }

    fun addDamage(key: Any, value: Double): DamageMemory {
        setLabel(Source(key, value), value)
        return this
    }

    fun takeDamage(key: Any, value: Double): DamageMemory {
        addDamage(key, -value)
        return this
    }

    fun addDamage(value: Double): DamageMemory {
        damage += value
        return this
    }

    override fun toString(): String {
        return "DamageMemory(attacker=$attacker, injured=$injured, totalDamage=$totalDamage)"
    }


    open class Source(val any: Any, var value: Double) {
        override fun toString(): String {
            return "Source(any=$any, value=$value)"
        }
    }

    class FinalSource(any: Any, value: Double) : Source(any, value)

}
